#pragma once
#include <iostream>
#include <vector>
#include <jsoncpp/json/json.h>
#include "util.hpp"
#include "Online.hpp"

class Room
{
    using cp = websocketpp::server<websocketpp::config::asio>::connection_ptr;
    enum Status {GAME_START, GAME_OVER};
    const int BOARD_ROW = 15;
    const int BOARD_COL = 15;

    const int WHITE_FLAG = 1;
    const int BLACK_FLAG = 2;
private:
    bool Direction(int color, int row, int col, int rowoff, int coloff)
    {
        int count = 1;
        for(int newrow = row + rowoff, newcol = col + coloff; 
        newrow >= 0 && newrow < BOARD_ROW && newcol >= 0 && newcol < BOARD_COL;
        newrow += rowoff, newcol += coloff)
        {
            if(_board[newrow][newcol] == color) ++count;
            else break;
        }

        for(int newrow = row - rowoff, newcol = col - coloff; 
        newrow >= 0 && newrow < BOARD_ROW && newcol >= 0 && newcol < BOARD_COL;
        newrow -= rowoff, newcol -= coloff)
        {
            if(_board[newrow][newcol] == color) ++count;
            else break;
        }

        return count >= 5;
    }
    bool IsWin(int color, int row, int col)
    {
        // 横
        bool horizen = Direction(color, row, col, 0, 1);

        //纵
        bool vertical = Direction(color, row, col, 1, 0);

        // 主对角线
        bool priciple_diagonal = Direction(color, row, col, -1, -1);

        // 副对角线
        bool sub_diagonal = Direction(color, row, col, 1, -1);

        return horizen || vertical || priciple_diagonal || sub_diagonal;

        // // 横行
        // int count = 0;
        // for(int i = col; i < BOARD_COL; ++i)
        // {
        //     if(_board[row][i] == color) count++;
        //     else  break;
        // }
        // for(int i  = col-1; i >= 0; --i)
        // {
        //     if(_board[row][i] == color) count++;
        //     else  break;
        // }
        // if(count >= 5) return true;

        // // 纵行
        // count = 0;
        // for(int i = row; i < BOARD_ROW; ++i)
        // {
        //     if(_board[i][col] == color) count++;
        //     else  break;
        // }
        // for(int i  = row-1; i >= 0; --i)
        // {
        //     if(_board[i][col] == color) count++;
        //     else  break;
        // }
        // if(count >= 5) return true;

        // // 主对角线
        // count = 0;
        // for(int i = 0; row>=i && col>=i; ++i)
        // {
        //     if(_board[row-i][col-i] == color) count++;
        //     else break;
        // }
        // for(int i = 1; row+i<BOARD_ROW && col+i<BOARD_COL; ++i)
        // {
        //     if(_board[row+i][col+i] == color) count++;
        //     else  break;
        // }
        // if(count >= 5) return true;

        // // 副对角线
        // count = 0;
        // for(int i = 0; row>=i && col+i<BOARD_COL; ++i)
        // {
        //     if(_board[row-i][col+i] == color) count++;
        //     else break;
        // }
        // for(int i = 1; row+i<BOARD_ROW && col>=i; ++i)
        // {
        //     if(_board[row+i][col-i] == color) count++;
        //     else  break;
        // }
        // if(count >= 5) return true;

        // return false;
    }

public:
    Room(int room_id, UserCtl* uc, Online_User_Manage* oum)
        :_room_id(room_id)
        ,_uc(uc)
        ,_oum(oum)
        ,_status(GAME_START)
        ,_player_num(0)
        ,_board(BOARD_ROW, std::vector<char>(BOARD_COL, 0))
    {
        LOG(INFO, "create room: %d, success\n", _room_id);
    }
    ~Room()
    {
        LOG(INFO, "destroy room: %d, success\n", _room_id);
    }
    
    int GetRoomid() {return _room_id;}
    Status GetStatus(){return _status;}
    int GetPlayerNum(){return _player_num;}
    int GetWhiteId(){return _white_id;}
    int GetBlackId(){return _black_id;}

    void AddWhitePlayer(int id)
    {
        _white_id = id;
        _player_num++;
    }
    void AddBlackPlayer(int id)
    {
        _black_id = id;
        _player_num++;
    }
    // {"optype": "put_chess","room_id": 222,"uid": 1,"row": 3,"col": 2}

    // {"optype": "put_chess", "result": false, "reason": "⾛棋失败具体原因...."}
    // {1、"optype": "put_chess",2、"room_id": 222,3、"uid": 1,4、"row": 3,5、"col": 2,6、"result": true
    // , 7、"reason":"","8、winner": 0 // 0-未分胜负， !0-已分胜负 (uid是谁，谁就赢了)
    Json::Value handle_chess(Json::Value& req)
    {
        int room_id = req["room_id"].asInt();
        int uid = req["uid"].asInt();
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();

        Json::Value res;
        res["optype"] = "put_chess";
        res["result"] = true;
        res["room_id"] = room_id;
        res["row"] = chess_row;
        res["col"] = chess_col;
        res["winner"] = 0;
        res["uid"] = uid;
        res["reason"] = "";
        
        // 1、判断房间号是否正确
        if(room_id != _room_id)
        {
            res["result"] = false;
            res["reason"] = "当前房间号错误";

            return res;
        }

        // 2、判断是否有人退出房间
        if(!_oum->is_in_game_room(_white_id))
        {
            // res["result"] = true;
            res["reason"] = "白棋用户投降，黑方获胜";
            res["uid"] = _black_id;
            res["row"] = -1;
            res["col"] = -1;

            res["winner"] = 1;

            _uc->Win(_black_id);
            _uc->Lose(_white_id);
            return res;
        }
        else if(!_oum->is_in_game_room(_black_id))
        {
            res["reason"] = "黑棋用户投降，白方获胜";
            res["uid"] = _white_id;

            res["winner"] = 1;
            res["row"] = -1;
            res["col"] = -1;

            _uc->Win(_white_id);
            _uc->Lose(_black_id);
            return res;
        }

        // 3、判断走棋位置是否合理
        if(_board[chess_row][chess_col] != 0)
        {
            res["result"] = false;
            res["reason"] = "当前位置不合法";

            return res;
        }
        // 走棋
        int color = uid == _white_id? WHITE_FLAG : BLACK_FLAG;
        _board[chess_row][chess_col] = color;

        // 4、判断是否胜利
        if(IsWin(color, chess_row, chess_col))
        {
            res["reason"] = "五星连珠，胜利！";
            res["uid"] = uid;

            res["winner"] = 1;
            
            int winner_id = uid;
            int loser_id = uid == _white_id ? _black_id : _white_id;
            _uc->Win(winner_id);
            _uc->Lose(loser_id);
            return res;
        }

        return res;
    }

    // {"optype": "chat","result": false"reason": "聊天失败具体原因....⽐如有敏感词..."}
    // {"optype": "chat","result": true,"room_id": 222,"uid": 1,"message": "赶紧点"}
    Json::Value handle_chat(Json::Value& req)
    {
        Json::Value resp = req;
        int room_id = req["room_id"].asInt();

        if(room_id != _room_id)
        {
            resp["result"] = false;
            resp["reason"] = "房间号不匹配";
            return resp;
        }

        // 检测敏感词 ... 

        resp["result"] = true;
        return resp;
    }

    void exit_room(int id)
    {
        Json::Value resp;
        if(_status == GAME_START)
        {
            resp["optype"] = "put_chess";
            resp["result"] = true;
            resp["room_id"] = _room_id;
            resp["winner"] = 1;
            resp["uid"] = id == _white_id ? _black_id : _white_id;
            resp["reason"] = "对面玩家投降，获得胜利";
            resp["row"] = -1;
            resp["col"] = -1;
            _uc->Win(_white_id);
            _uc->Lose(_black_id);
            broadcast(resp);
            _status = GAME_OVER;
        }

        _player_num--;
    }

    void handle_req(Json::Value& req)
    {
        Json::Value resp = req;

        if(resp["optype"].asString() == "put_chess")
        {
            resp = handle_chess(req);
        }
        else if(resp["optype"].asString() == "chat")
        {
            resp = handle_chat(req);
        }
        else
        {
            resp["result"] = false;
            resp["reason"] = "请求类型不匹配";
        }

        broadcast(resp);
    }

    void broadcast(Json::Value& resp)
    {
        std::string msg;       
        if(!json_util::serialize(resp, msg))
        {
            LOG(INFO, "房间信息广播失败\n");
            return;
        }

        LOG(INFO, "广播信息：%s\n", msg.c_str());

        cp wconn = _oum->get_connect_from_room(_white_id);
        if(wconn.get() != nullptr)
        {
            wconn->send(msg);
        }
        cp bconn = _oum->get_connect_from_room(_black_id);
        if(bconn.get() != nullptr)
        {
            bconn->send(msg);
        }
    }
private:
    int _room_id;
    Status _status; 
    int _player_num;
    int _white_id;
    int _black_id;
    std::vector<std::vector<char>> _board;

    UserCtl* _uc;
    Online_User_Manage* _oum;
};

using room_ptr = std::shared_ptr<Room>;

class RoomManage
{
public:
    RoomManage(UserCtl* uc, Online_User_Manage* oum)
        :_next_rid(1), _uc(uc), _oum(oum)
    {}
    ~RoomManage()
    {}

    room_ptr create_room(int uid1, int uid2)
    {
        if(!_oum->is_in_game_hall(uid1))
        {
            LOG(ERROR, "user: %d is not in game hall\n", uid1);
            return room_ptr();
        }

        if(!_oum->is_in_game_hall(uid2))
        {
            LOG(ERROR, "user: %d is not in game hall\n", uid2);
            return room_ptr();
        }

        std::unique_lock<std::mutex> lock(_mtx);
        room_ptr rp(new Room(_next_rid, _uc, _oum));
        rp->AddBlackPlayer(uid1);
        rp->AddWhitePlayer(uid2);
        _rooms.insert(std::make_pair(_next_rid, rp));
        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));

        _next_rid++;

        return rp;
    }
    void destroy_room(int rid)
    {
        room_ptr rp = get_room_by_roomid(rid);

        std::unique_lock<std::mutex> lock(_mtx);

        _users.erase(rp->GetBlackId());
        _users.erase(rp->GetWhiteId());

        _rooms.erase(rid);
    }

    room_ptr get_room_by_roomid(int rid)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        auto it = _rooms.find(rid);
        if(it == _rooms.end()) return room_ptr();

        return it->second;
    }
    room_ptr get_room_by_userid(int uid)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        auto uit = _users.find(uid);
        if(uit == _users.end()) 
        {
            LOG(INFO, "uid: %d, 没有找到该用户\n", uid);
            return room_ptr();
        }

        int rid = uit->second;
        auto rit = _rooms.find(rid);
        if(rit == _rooms.end()) 
        {
            LOG(INFO, "roomid: %d, 没有找到该房间\n", rid);

            return room_ptr();
        }

        return rit->second;
    }

    // 删除指定用户
    void remove_room_user(int uid)
    {
        room_ptr rp = get_room_by_userid(uid);

        rp->exit_room(uid);
        if(rp->GetPlayerNum() == 0)
        {
            destroy_room(rp->GetRoomid());
            return;
        }

        std::unique_lock<std::mutex> lock(_mtx);
        _users.erase(uid);
    }

    
private:
    int _next_rid;
    std::mutex _mtx;
    UserCtl* _uc;
    Online_User_Manage* _oum;
    std::unordered_map<int, room_ptr> _rooms;// roomid -> room
    std::unordered_map<int, int> _users; // userid -> roomid
};
